package team.star.werespos.config.shiro;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;

/**
 * ShiroConfiguration class<br/>
 * 自定义Shiro验证
 * @author hdonghong
 * @since 2018/04/30
 */
@Configuration
public class ShiroConfiguration {

    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager manager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();

        // public enum DefaultFilter {...}，key为接口；value为规则
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();

        // roles[admin]：表示用户需要有admin角色；perms[update]：表示用户需要有update权限
        filterChainDefinitionMap.put("/admin/user/**", "roles[admin]");
        filterChainDefinitionMap.put("/admin/role/**", "roles[admin]");
        filterChainDefinitionMap.put("/admin/permission/**", "roles[admin]");
        filterChainDefinitionMap.put("/**/new", "perms[new]");
        filterChainDefinitionMap.put("/admin/**/list", "perms[list]");
        filterChainDefinitionMap.put("/admin/**/profile", "perms[profile]");
        filterChainDefinitionMap.put("/**/update", "perms[update]");
        filterChainDefinitionMap.put("/**/delete", "perms[delete]");

        // anon：表示不需要登录即可访问
        // authc：用于实现基于表单的身份验证。当key为/**时，anon需要先存入
        // user：认证过滤器，表示必须存在用户，当登入操作时不做检查。即用户不一定需要已经通过认证,只需要曾经被Shiro记住过登录状态(rememberMe)就可以正常访问。
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/admin/**", "authc");
//        filterChainDefinitionMap.put("/admin/**", "user");

        // 配置注销页面，访问该链接会自动实现注销（退出）功能
        filterChainDefinitionMap.put("/logout", "logout");

        bean.getFilters().put("authc", new MyFormAuthenticationFilter());
        bean.getFilters().put("roles", new MyRoleAuthenticationFilter());
        bean.getFilters().put("perms", new MyPermissionAuthenticationFilter());

        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        bean.setSecurityManager(manager);
/*        // 登录接口
        bean.setLoginUrl("/toLogin");
        // 验证成功接口
        bean.setSuccessUrl("/index");
        // 无权限
        bean.setUnauthorizedUrl("/unauthorized");*/
        return bean;
    }

    // SecurityManager：安全管理器；即所有与安全有关的操作都会与 SecurityManager 交互；且管理着所有 Subject；
    // 它是 Shiro 的核心，负责与后边介绍的其他组件进行交互，类似于SpringMVC 中的 DispatcherServlet 前端控制器；
    @Bean("securityManager")
    public DefaultWebSecurityManager securityManager(
            @Qualifier("authRealm") AuthRealm authRealm) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(authRealm);
        return manager;
    }

    // Realm：域，Shiro 从 Realm 获取安全数据（如用户、角色、权限），就是说 SecurityManager 要验证用户身份，
    // 那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法；也需要从 Realm 得到用户相应的角色 / 权限
    // 进行验证用户是否能进行操作；可以把 Realm 看成 DataSource，即安全数据源。
    @Bean("authRealm")
    public AuthRealm authRealm(
            @Qualifier("credentialMatcher") CredentialMatcher credentialMatcher) {
        AuthRealm authRealm = new AuthRealm();
        authRealm.setCredentialsMatcher(credentialMatcher);
        return authRealm;
    }

    // 自定义的密码校验
    @Bean("credentialMatcher")
    public CredentialMatcher credentialMatcher() {
        return new CredentialMatcher();
    }

    // 用于开启Shiro Spring AOP权限注解的支持
    // 处理shiro和spring关联，让spring管理shiro时使用我们自定义的SecurityManager
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            @Qualifier("securityManager") SecurityManager manager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(manager);
        return advisor;
    }

    // 处理shiro和spring关联，使用AOP代理类
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }

}
